
module jade.tpl.js.JType ;

import jade.tpl.Template ;

final class JType(Base) : Dobject {
	
	static if( is(Base==struct) ){
		alias Base* pThis;
	}else static if( is(Base==class) ){
		alias Base pThis;
	} else {
		static assert(false);
	}
	alias JType!(Base)	JType_Obj ;
	alias FieldTypeTuple!(Base) TupleList ;

	static {
		const string ClassName	= "D_" ~ Base.stringof ;
		int[char[]] tuple_id ;
		Value[Base.tupleof.length] _tuple_name ;
		Program program = null ;
		Obj_Constructor		Obj_constructor	= null ;
		Obj_Prototype		Obj_prototype	= null ;
		NativeFunctionData 	static_nfd[] ;
		NativeFunctionData 	protype_nfd[]	= [
				{ TEXT_forEach , &Obj_rototype_forEach, 0} ,
			] ;
	}
	
	static void* Obj_rototype_forEach(Dobject pthis, CallContext* cc, Dobject othis, Value* ret, Value[] args) {
		static const func_name = ClassName ~ ".forEach";
		ErrInfo errinfo;
		JType_Obj _this	= cast(JType_Obj) othis ;
		if( _this is null ) {
                	return RuntimeError(&errinfo, ERR_TYPE_ERROR , func_name);
		}
		
		if( args.length > 0 && args[0].isObject ){
			auto fn	= cast(Dfunction) args[0].toObject ;
			Dobject call_this = othis ;
			if( args.length > 1 && args[1].isObject ){
				call_this	= args[1].toObject ;
			}
			if( fn !is null) {
				foreach(int i, _type; TupleList) {
					const name	= Base.tupleof[i].stringof[ Base.stringof.length + 3.. $] ;
					scope Value 	_ret;
					scope Value[2]	_args ;
					static if( is(_type==bool) ) {
						_args[0].putVboolean( _this._obj.tupleof[i]  ) ;
					} else static if( isSomeString!(_type) ) {
						_args[0].putVstring( _this._obj.tupleof[i]  ) ;
					} else static if( std.traits.isNumeric!(_type) ) {
						_args[0].putVnumber( _this._obj.tupleof[i]  ) ;
					} else {
						static assert(false, _type.stringof ) ;
					}
					
					_args[1].putVstring(name);
					
					Value* __ret ;
					__ret = cast(Value*) fn.Call( Tpl_Factory.program.callcontext, call_this, &_ret, _args );
					
					if( __ret !is null ){
						Log( __ret.toString );
						assert(false);
					}
				}
				return null ;
			}
		}
		assert(false);
		return RuntimeError(&errinfo, ERR_TYPE_ERROR , func_name);
	}
	
	static final class Obj_Constructor  : Dfunction {
		final this() {
			super(Base.tupleof.length , Dfunction_prototype);
			name = ClassName ;
			
			DnativeFunction.init(this, static_nfd, DontEnum);
		}
		
		final void* Construct(CallContext *cc, Value *ret, Value[] arglist) {
			assert(false,  ClassName ~ " can't call Constructor");
			return null;
		}

		final void* Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
		{
			assert(false,  ClassName ~ " can't call Constructor");
			return null;
		}
	}
	
	static final class Obj_Prototype : Dobject {
		Darray	_tupleof ;
		final this() {
			super(Dobject_prototype);
			Put("classname" , ClassName, DontEnum| DontDelete | ReadOnly);
			
			DnativeFunction.init(this, protype_nfd, DontEnum);
			assert(proptable.get("toString", Value.calcHash("toString"))) ;
			
			_tupleof	= new Darray(Dobject_prototype);
			
			foreach(int i, _type; TupleList) {
				const name	= Base.tupleof[i].stringof[ Base.stringof.length + 3.. $] ;
				tuple_id[name] = i ;
				_tuple_name[i].putVstring(name);
				// Log("%s", _tuple_name[i].toString);
				_tupleof.Put(cast(d_uint32) i, &_tuple_name[i], DontDelete | ReadOnly );
			}
			Put( "tupleof", _tupleof, DontEnum| DontDelete | ReadOnly);
		}
		
	}
	
	static void Init(Program p) {
		if( Obj_constructor is null ) {
			Obj_constructor	= new Obj_Constructor() ;
			Obj_prototype	= new Obj_Prototype() ;
		}
		if( program is null ){
			program	= p ;
			p.callcontext.global.Put( ClassName, Obj_constructor, DontEnum | DontDelete | ReadOnly ) ;
		}
	}

	pThis	_obj	= null ;
	Value[Base.tupleof.length ] _tuple ;
	
	final this(Dobject prototype)
	{
		assert(prototype is Dobject_prototype);
		super(prototype);
		assert(false);
	}
	
	final this(pThis obj)
	{
		super(Obj_prototype);
		classname = ClassName ;
		_obj	= obj ;
	}
	
	final Value* Get(d_string PropertyName, uint hash){
		int* id_ptr     = PropertyName in tuple_id;
		if( id_ptr is null ) {
			return super.Get(PropertyName, hash) ;
		}
		int id		= *id_ptr;
		auto ret	= &_tuple[id] ;
		static if(is(Base==class)){
			if( _obj is null ){
				return ret;
			}
		}
		// if( ret.isUndefined ) 
			load_value(id);
		
		return  ret ;
	}
	
	private final void load_value(int id) {
		foreach(int i, _type; TupleList) {
			const name	= Base.tupleof[i].stringof[ Base.stringof.length + 3.. $] ;
			if( id is i ) {
				static if( is(_type==bool) ) {
					_tuple[i].putVboolean( _obj.tupleof[i]  ) ;
				} else static if( isSomeString!(_type) ) {
					_tuple[i].putVstring( _obj.tupleof[i]  ) ;
				} else static if( std.traits.isNumeric!(_type) ) {
					_tuple[i].putVnumber( _obj.tupleof[i]  ) ;
				} else {
					static assert(false, _type.stringof ) ;
				}
			}
		}
	}
}
